#ifndef REDLUA_HPP
#define REDLUA_HPP

/********************************************************************
	created:	2011/01/19
	filename: 	E:\C++\Red\src\Engine\Lua\LuaState.hpp
	author:		Red
	
	purpose:	Wrapper class between Lua and C++
*********************************************************************/

#include "LuaValue.hpp"
#include "Lunar.hpp"

namespace red {
	/// Lua function template
	/// Style :  int functionName(lua_State* L);
	typedef lua_CFunction LuaFunctionType;

	/// Wrapper class for Lua RAII
	class REDAPI Lua{
	public:
		Lua();
		~Lua();

		/// Returns implicitly the lua_State pointer
		operator lua_State*() { return mState; }

		/// Load a given script on the stack
		/// @param pName : Script FileName
		/// @return : True if the script has been successfully loaded
		bool LoadScript(const std::string &pName);

		/// Register a lua function
		/// @param pName : Function name callable in the lua script
		/// @param pFunction : Function definition
		void RegisterFunction(const std::string &pName, LuaFunctionType pFunction);

		/// Check if the sended lua status int caused an error
		/// @param pStatus : status to check
		/// @return : True if an error occured
		bool CheckError(s32 pStatus);

		/// Just call the Lua GarbageCollector at any moment
		void CollectGarbage();

		/// Call a Lua function from C++
		/// @param pFuncName : Function name in the Lua Script
		/// @param pParams   : Optional parameters
		/// @return : a LuaValue castable
		LuaValue CallFunction(const std::string &pFuncName, const LuaValueTable &pParams = LuaValueTable());

		/// Retrieve a Global Variable from a Lua Script
		/// @param pVariable : name of the wanted variable
		/// @return : a LuaValue castable
		LuaValue GetGlobalVar(const std::string &pVariable);



		// Static functions used in defined functions
			/// Returns the argument number from a function
			static s32 GetArgCount(lua_State* pState);
/*
			/// Returns the n-th argument from a function
				/// Generic One (Throws exception)
				template<class T>
				static T GetArgument(lua_State* pState, s32 pPos);

				/// For String
				template<>
				static std::string GetArgument(lua_State* pState, s32 pPos);

				/// For Int
				template<>
				static s32 GetArgument(lua_State* pState, s32 pPos);

				/// For Float
				template<>
				static f32 GetArgument(lua_State* pState, s32 pPos);

				/// For Bool
				template<>
				static bool GetArgument(lua_State* pState, s32 pPos);

			/// Send a return value to the script
				/// Generic One (Throws exception)
				template<class T>
				static void SendValue(lua_State* pState, T pValue);

				/// For String
				template<>
				static void SendValue(lua_State* pState, const char* pValue);

				/// For Int
				template<>
				static void SendValue(lua_State* pState, s32 pValue);

				/// For Float
				template<>
				static void SendValue(lua_State* pState, f32 pValue);

				/// For Bool
				template<>
				static void SendValue(lua_State* pState, bool pValue);
*/

	private:
		lua_State* mState;		///< Lua State used for the whole program
	};
/*
	#pragma region TemplateDefinitions

	template<class T>
	inline void Lua::SendValue( lua_State* pState, T pValue ){
		throw Exception("Red::LuaError::SendValue() : Unknown variable type sent.");
	}

	template<>
	inline void Lua::SendValue( lua_State* pState, const char* pValue ){
		lua_pushstring(pState, pValue);
	}

	template<>
	inline void Lua::SendValue( lua_State* pState, s32 pValue ){
		lua_pushinteger(pState, pValue);
	}

	template<>
	inline void Lua::SendValue( lua_State* pState, f32 pValue ){
		lua_pushnumber(pState, (f64)pValue);
	}

	template<>
	inline void Lua::SendValue( lua_State* pState, bool pValue ){
		lua_pushboolean(pState, pValue);
	}

	template<>
	inline std::string Lua::GetArgument( lua_State* pState, int pPos ){
		if(pPos > Lua::GetArgCount(pState) - 1)
			throw Exception(String("Red::LuaError::GetArgument() : ")+pPos+" is not a valid position");

		return lua_tostring(pState, pPos + 1);
	}

	template<>
	inline s32 Lua::GetArgument( lua_State* pState, int pPos ){
		if(pPos > Lua::GetArgCount(pState) - 1)
			throw Exception(String("Red::LuaError::GetArgument() : ")+pPos+" is not a valid position");

		return lua_tointeger(pState, pPos + 1);
	}

	template<>
	inline f32 Lua::GetArgument( lua_State* pState, int pPos ){
		if(pPos > Lua::GetArgCount(pState) - 1)
			throw Exception(String("Red::LuaError::GetArgument() : ")+pPos+" is not a valid position");

		return (f32)lua_tonumber(pState, pPos + 1);
	}

	template<>
	inline bool Lua::GetArgument( lua_State* pState, int pPos ){
		if(pPos > Lua::GetArgCount(pState) - 1)
			throw Exception(String("Red::LuaError::GetArgument() : ")+pPos+" is not a valid position");

		return lua_toboolean(pState, pPos + 1) > 0;
	}

	template<class T>
	inline T Lua::GetArgument(lua_State* pState, int pPos){
		throw Exception("Red::LuaError::GetArgument() : Unknown Variable type wanted.");
	}
	#pragma endregion
*/
}

#endif // LUA_HPP
